import type { Layer, SerializableObject } from "@/core/public";
import { CanvasContextType } from "./public";
import { AppDispatch } from "@/store";
import layerSlice from "@/reducers/layerSlice";
import EventEmitter from '@/utils/events'

export type RendererStatus = 'pending' | 'rendering' | 'willSleep' | 'sleeping';
export type IsContinueRender = boolean;

let autoIndex = 0;
let instance: Renderer;
const rendererEmitter = new EventEmitter();  // 全局事件

export default class Renderer {
  id: number = 0
  mainCtx: CanvasRenderingContext2D | null = null
  rasterCtx: CanvasRenderingContext2D | null = null
  // thumbCtx: CanvasRenderingContext2D | null = null
  layers: Layer[] = []
  status: RendererStatus = 'pending'
  requestAnimationFrameId: number = 0
  sleepDuration: number = 3000
  sleepTimer?: ReturnType<typeof setTimeout>

  constructor(){
    if(instance) return instance
    this.id = ++autoIndex
    this.startRender()
  }

  protected beforeRender(){
    const mainCtx = this.mainCtx;
    const rasterCtx = this.rasterCtx;
    // const thumbCtx = this.thumbCtx;
    if(mainCtx) mainCtx.clearRect(0, 0, mainCtx.canvas.width, mainCtx.canvas.height)
    if(rasterCtx) rasterCtx.clearRect(0, 0, rasterCtx.canvas.width, rasterCtx.canvas.height)
    // if(thumbCtx) thumbCtx.clearRect(0, 0, thumbCtx.canvas.width, thumbCtx.canvas.height)
  }

  protected onceRender(force: boolean = false){
    // 渲染前
    this.beforeRender();
    
    // 渲染队列
    const renderTaskQueue: Array<boolean> = this.layers.map((layer) => {
      const ctx = this.getLayerCtx(layer);
      if(force) layer.rendered = false
      if(ctx) return layer.render(ctx)
      else return false
    })

    // 如果包含需要继续渲染的任务，则进入下一次渲染
    const isNeedContinueRender = renderTaskQueue.some((taskCellIsContinue) => taskCellIsContinue)
    return isNeedContinueRender
  }

  protected willIntoSleep(){
    clearTimeout(this.sleepTimer)
    this.setStatus('willSleep')
    this.sleepTimer = setTimeout(() => {
      this.setStatus('sleeping')
      this.stopRender()
    }, this.sleepDuration);
  }

  protected setStatus(status: RendererStatus){
    this.status = status
    console.log(status);
    
    rendererEmitter.emit('statusChange', status)
  }

  startRender(force: boolean = false){
    if(this.status == 'willSleep') {    // 待机状态时，重新激活并跳过
      clearTimeout(this.sleepTimer)
      this.setStatus('rendering')
      return
    }
    else if(this.status == 'rendering') return // 正在渲染，跳过

    let isFirstRender = true;

    const action = () => {
      const isContinue = this.onceRender(isFirstRender ? force : false)
      isFirstRender = false  // 仅强制渲染一次

      // 未休眠时，持续渲染
      if(this.status != 'sleeping') this.requestAnimationFrameId = requestAnimationFrame(action)

       // 不再需要渲染后，即将进入休眠状态
      if(this.status == 'rendering' && !isContinue) this.willIntoSleep()
    }

    this.setStatus('rendering')
    action()
  }

  stopRender(){
    cancelAnimationFrame(this.requestAnimationFrameId)
  }

  add(layer: Layer, dispatch?: AppDispatch){
    this.layers.push(layer)
    renderer.startRender()
    if(dispatch) dispatch(layerSlice.actions.add(layer))
  }

  remove(layerId: number, dispatch?: AppDispatch){
    this.layers = this.layers.filter(item => item.id !== layerId)
    renderer.startRender()
    if(dispatch) dispatch(layerSlice.actions.remove(layerId))
  }

  update(layerId: number, options: SerializableObject, dispatch?: AppDispatch){
    let layer = this.getLayer(layerId)
    if(layer){
      Object.keys(options).forEach(key => {
        if(key in layer){
          layer[key] = options[key]
        }
      })
      layer.rendered = false
      renderer.startRender()
      if(dispatch) dispatch(layerSlice.actions.update(layer.serialize()))
    }
  }

  reset(dispatch?: AppDispatch){
    this.layers = []
    renderer.startRender()
    if(dispatch) dispatch(layerSlice.actions.reset())
  }

  hasLayer(layerId: number){
    return !!this.layers.find(item => item.id === layerId)
  }

  getLayer(layerId: number){
    return this.layers.find(item => item.id === layerId)
  }

  getLayerCtx(layer: Layer){
    switch(layer.ctxType){
      case 'main': return this.mainCtx
      case 'raster': return this.rasterCtx
      // case 'thumb': return this.thumbCtx
      default: return null
    }
  }

  mountCtx(ctxType: CanvasContextType, ctx: CanvasRenderingContext2D | null){
    switch(ctxType){
      case 'main': {
        this.mainCtx = ctx;
        break;
      }
      case 'raster': {
        this.rasterCtx = ctx
        break;
      }
      // case 'thumb': {
      //   this.thumbCtx = ctx
      //   break;
      // }
    }

    if(this.layers.length) this.startRender()
  }

  on = rendererEmitter.on
}

export const renderer = new Renderer();

